#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

////@begin includes

//#include <highgui.h>
#include <wx/filedlg.h>
#include <wx/filename.h>
#include "camclientframe.h"
#include "sessionslistframe.h"
#include "trainingframe.h"
#include "Determinator.h"


#define WAIT_FOR_NEXT_FRAME 100

IMPLEMENT_CLASS( ContourPanel, wxScrolledWindow )

BEGIN_EVENT_TABLE( ContourPanel, wxScrolledWindow )
	EVT_PAINT(ContourPanel::OnPaint)
	EVT_SIZE(ContourPanel::OnSize)
END_EVENT_TABLE()

ContourPanel::ContourPanel(wxWindow *parent, wxWindowID winid /* = wxID_ANY */, const wxPoint& pos /* = wxDefaultPosition */, const wxSize& size /* = wxDefaultSize */, long style /* = wxTAB_TRAVERSAL | wxNO_BORDER */, const wxString& name /* = wxPanelNameStr */ )
: wxScrolledWindow(parent, winid, pos, size, style, name) {
	this->bmp = 0;
}

ContourPanel::~ContourPanel() {
	if (this->bmp != 0) {
		delete this->bmp;
	}
}

void ContourPanel::SetBitmap(wxBitmap * bmp) {
	if (this->bmp != NULL) {
		delete this->bmp;
	}
	this->bmp = bmp;
}

void ContourPanel::OnPaint(wxPaintEvent & event) {
	wxPaintDC pdc(this);

/*#if wxUSE_GRAPHICS_CONTEXT
	wxGCDC gdc( pdc ) ;
	wxDC &dc = m_useContext ? (wxDC&) gdc : (wxDC&) pdc ;
#else*/
	wxDC &dc = pdc ;
//#endif

	PrepareDC(dc);

	this->GetParent()->PrepareDC(dc);

	if (this->bmp != NULL) {
		dc.DrawBitmap(*(this->bmp), 0, 0);
	}
}

void ContourPanel::OnSize(wxSizeEvent& event) {
	this->RecalcScrollBars();	
}

void ContourPanel::RecalcScrollBars() {
	int countScrollWidth = 0, countScrollHeight = 0;
	int scrollStepWidth = 10, scrollStepHeight = 10;
	if (this->bmp != NULL) {
		wxSize size = this->GetClientSize();
		countScrollWidth = (this->bmp->GetWidth()) / scrollStepWidth;
		countScrollHeight = (this->bmp->GetHeight()) / scrollStepHeight;
	}
	this->SetScrollbars(scrollStepWidth, scrollStepHeight, 
		countScrollWidth < 0 ? 0 : countScrollWidth, 
		countScrollHeight < 0 ? 0 : countScrollHeight
	);
}

////@end includes

////@begin XPM images
////@end XPM images

IMPLEMENT_CLASS( CamClientFrame, wxFrame )

BEGIN_EVENT_TABLE( CamClientFrame, wxFrame )
	EVT_BUTTON(ID_LOAD_CONTOUR, CamClientFrame::OnOpenContour)
	EVT_BUTTON(ID_START_VIDEO_STREAM, CamClientFrame::OnStartVideoStream)
	EVT_BUTTON(ID_STOP_VIDEO_STREAM, CamClientFrame::OnStopVideoStream)
	EVT_BUTTON(ID_SHOW_THIS_VIDEO, CamClientFrame::OnOpenThisCamera)
	EVT_BUTTON(ID_MAKE_PHOTO, CamClientFrame::OnMakePhoto)
	EVT_BUTTON(ID_LOAD_SESSION, CamClientFrame::OnOpenExistingSession)
	EVT_BUTTON(ID_NEW_TRAINING, CamClientFrame::OnNewTraining)
	EVT_CLOSE(CamClientFrame::OnClose)
	EVT_TIMER(SHOW_FRAME_TIMER_ID, CamClientFrame::OnTimer)
	EVT_TIMER(SHOW_THIS_TIMER_ID, CamClientFrame::OnThisTimer)
END_EVENT_TABLE()

const char * CamClientFrame::videoWindowName = "Video stream";

CamClientFrame::CamClientFrame()
{
    Init();
}

CamClientFrame::CamClientFrame(wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style )
: videoFrameTimer(this, SHOW_FRAME_TIMER_ID)
, videoThisTimer(this, SHOW_THIS_TIMER_ID)
{
    Init();
    Create( parent, id, caption, pos, size, style );
}

bool CamClientFrame::Create( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style )
{
    wxFrame::Create( parent, id, caption, pos, size, style );
    CreateControls();
    Centre();
    return true;
}

CamClientFrame::~CamClientFrame() 
{
}

void CamClientFrame::Init() 
{
    this->contourPanel = 0;
    this->openContourButton = 0;
	this->videoThread = 0;
	this->startVideoStream = 0;
	this->stopVideoStream = 0;
	this->makePhoto = 0;
	this->thisCapture = 0;
	ConturFr = 0;
}

void CamClientFrame::CreateControls()
{
	CamClientFrame* itemFrame1 = this;

	wxBoxSizer* itemBoxSizer2 = new wxBoxSizer(wxVERTICAL);
	itemFrame1->SetSizer(itemBoxSizer2);

	wxBoxSizer* itemBoxSizer3 = new wxBoxSizer(wxHORIZONTAL);
	itemBoxSizer2->Add(itemBoxSizer3, 4, wxGROW|wxALL, 5);

	contourPanel = new ContourPanel( itemFrame1, ID_PANEL, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER|wxTAB_TRAVERSAL );
	contourPanel->SetName(_T("contourPanel"));
	itemBoxSizer3->Add(contourPanel, 1, wxGROW|wxALL, 5);

	wxBoxSizer* itemBoxSizer5 = new wxBoxSizer(wxHORIZONTAL);
	itemBoxSizer2->Add(itemBoxSizer5, 0, wxGROW|wxALL, 5);

	wxBoxSizer* itemBoxSizer6 = new wxBoxSizer(wxVERTICAL);
	itemBoxSizer5->Add(itemBoxSizer6, 1, wxGROW|wxALL, 5);

	wxBoxSizer* itemBoxSizer7 = new wxBoxSizer(wxHORIZONTAL);
	itemBoxSizer6->Add(itemBoxSizer7, 0, wxGROW|wxALL, 5);

	wxStaticText* itemStaticText8 = new wxStaticText( itemFrame1, wxID_STATIC, _("Server IP Address:"), wxDefaultPosition, wxDefaultSize, 0 );
	itemBoxSizer7->Add(itemStaticText8, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5);

	ipAddressText = new wxTextCtrl( itemFrame1, ID_TEXTCTRL, _("127.0.0.1"), wxDefaultPosition, wxDefaultSize, 0 );
	ipAddressText->SetName(_T("ipAddressText"));
	itemBoxSizer7->Add(ipAddressText, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5);

	wxBoxSizer* itemBoxSizer10 = new wxBoxSizer(wxVERTICAL);
	itemBoxSizer5->Add(itemBoxSizer10, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5);

	startVideoStream = new wxButton( itemFrame1, ID_START_VIDEO_STREAM, _("Start Video Stream"), wxDefaultPosition, wxDefaultSize, 0 );
	startVideoStream->SetName(_T("startVideoStream"));
	itemBoxSizer10->Add(startVideoStream, 0, wxGROW|wxALL, 5);

	stopVideoStream = new wxButton( itemFrame1, ID_STOP_VIDEO_STREAM, _("Stop Video Stream"), wxDefaultPosition, wxDefaultSize, 0 );
	stopVideoStream->SetName(_T("stopVideoStream"));
	stopVideoStream->Enable(false);
	itemBoxSizer10->Add(stopVideoStream, 0, wxGROW|wxALL, 5);

	openContourButton = new wxButton( itemFrame1, ID_LOAD_CONTOUR, _("Choose Search Contour..."), wxDefaultPosition, wxDefaultSize, 0 );
	openContourButton->SetName(_T("openContourButton"));
	itemBoxSizer10->Add(openContourButton, 0, wxGROW|wxALL, 5);

	showThisVideo = new wxButton( itemFrame1, ID_SHOW_THIS_VIDEO, _("Show This Video"), wxDefaultPosition, wxDefaultSize, 0 );
	showThisVideo->SetName(_T("showThisVideo"));
	itemBoxSizer10->Add(showThisVideo, 0, wxGROW|wxALL, 5);

	makePhoto = new wxButton( itemFrame1, ID_MAKE_PHOTO, _("Make Photo"), wxDefaultPosition, wxDefaultSize, 0 );
	makePhoto->SetName(_T("makePhoto"));
	itemBoxSizer10->Add(makePhoto, 0, wxGROW|wxALL, 5);

	openSessionButton = new wxButton( itemFrame1, ID_LOAD_SESSION, _("Open Existing Session..."), wxDefaultPosition, wxDefaultSize, 0 );
	openSessionButton->SetName(_T("openContourButton"));
	itemBoxSizer10->Add(openSessionButton, 0, wxGROW|wxALL, 5);

	trainClassificator = new wxButton( itemFrame1, ID_NEW_TRAINING, _("Train Classificator"), wxDefaultPosition, wxDefaultSize, 0 );
	trainClassificator->SetName(_T("trainClassificator"));
	itemBoxSizer10->Add(trainClassificator, 0, wxGROW|wxALL, 5);
}

bool CamClientFrame::ShowToolTips()
{
    return true;
}

wxBitmap CamClientFrame::GetBitmapResource( const wxString& name )
{
    wxUnusedVar(name);
    return wxNullBitmap;
}

wxIcon CamClientFrame::GetIconResource( const wxString& name )
{
    wxUnusedVar(name);
    return wxNullIcon;
}

void CamClientFrame::OnStartVideoStream(wxCommandEvent & event) {
	wxCriticalSectionLocker enter(this->videoThreadCS);
	this->OnThreadStart();
}

void CamClientFrame::OnStopVideoStream(wxCommandEvent & event) {
	wxCriticalSectionLocker enter(this->videoThreadCS);
	this->OnThreadStop();
}

void CamClientFrame::OnOpenContour(wxCommandEvent & event) {
	event.Skip(false);
	wxFileDialog dlg(
		this, wxString(_T("Choose contour file")),
		_(""), _(""), 
		_("JPEG files (*.jpeg;*.jpg)|*.jpeg;*.jpg|GIF Files (*.gif)|*.gif|BMP files (*.bmp)|*.bmp|PNG files (*.png)|*.png")
	);
	dlg.CentreOnParent();
	dlg.SetDirectory(wxGetHomeDir());
	if (dlg.ShowModal() == wxID_OK) {
		wxString filename = dlg.GetPath();
		if (ConturFr)
		{
			cvReleaseImage(&ConturFr);
		}

		char * file = new char[1024];
		wcstombs(file, filename.GetData(), -1);
		ConturFr = cvLoadImage(file);
		delete [] file;
		cvSmooth( ConturFr, ConturFr, CV_GAUSSIAN, 3, 3 );
		CvSize size = cvSize(ConturFr->width, ConturFr->height);
		IplImage* fGray = cvCreateImage(size,ConturFr->depth,1);
		cvCvtColor(ConturFr,fGray,CV_RGB2GRAY);		
		IplImage* fSegments = Segmentation::Treshold(fGray,10);	
		cvReleaseImage(&ConturFr);
		ConturFr = cvCreateImage(cvSize(fSegments->width/4,fSegments->height/4),fSegments->depth,1);
		cvResize(fSegments,ConturFr);
		cvReleaseImage(&fGray);
		cvReleaseImage(&fSegments);

		wxImage img(filename);
		wxBitmap * bmp = new wxBitmap(img);
		this->contourPanel->SetBitmap(bmp);
		this->contourPanel->RecalcScrollBars();
		this->contourPanel->Refresh();
	}
}

void CamClientFrame::OnTimer(wxTimerEvent & event) {
	wxCriticalSectionLocker enter(this->videoThreadCS);
	if (this->videoThread) {
		IplImage * image = this->videoThread->GetImage();
		this->ProcessVideoFrame(image);
	} else {
		this->OnServerDisconnect();
	}
}

void CamClientFrame::OnClose(wxCloseEvent & event) {
	wxCriticalSectionLocker enter(this->videoThreadCS);
	this->OnThreadStop();
	this->Destroy();
}

void CamClientFrame::OnThreadCompletion() {
	this->videoThread = 0;
}

void CamClientFrame::OnServerDisconnect() {
	this->OnThreadStop();
	wxMessageBox(wxString(_("Server disconnected. Video stream will be stop.")));
}

void CamClientFrame::OnThreadStart() {
	SocketClient * client = new SocketClient();
	if (client->openConnection(this->ipAddressText->GetValue().ToAscii(), 3000)) {
		cvNamedWindow(CamClientFrame::videoWindowName, CV_WINDOW_AUTOSIZE);
		this->videoFrameTimer.Start(WAIT_FOR_NEXT_FRAME);
		this->videoThread = new VideoThread(this, client);
		if (this->videoThread->Create() != wxTHREAD_NO_ERROR || this->videoThread->Run() != wxTHREAD_NO_ERROR) {
			wxMessageBox(_("Can\'t create video thread"));
			delete(this->videoThread);
			this->videoThread = 0;
			this->OnThreadStop();
			return;
		}
		this->stopVideoStream->Enable(true);
		this->startVideoStream->Enable(false);
	} else {
		delete client;
		wxMessageBox(_("Can\'t connect to remote video server"));
	}
}

void CamClientFrame::OnThreadStop() {
	this->videoFrameTimer.Stop();
	cvDestroyWindow(CamClientFrame::videoWindowName);
	if (this->videoThread) {
		if (this->videoThread->Delete() != wxTHREAD_NO_ERROR) {
			wxMessageBox(_("Can\'t delete video thread!"));
		}
		this->videoThread = 0;
	}
	this->stopVideoStream->Enable(false);
	this->startVideoStream->Enable(true);
}

void CamClientFrame::ProcessVideoFrame(IplImage * image) {

	if (ConturFr)
	{	
		cvSmooth( image, image, CV_GAUSSIAN, 3, 3 );
		CvSize size = cvSize(image->width, image->height);
		IplImage* fGray = cvCreateImage(size,image->depth,1);	
		cvCvtColor(image,fGray,CV_RGB2GRAY);	
		IplImage* fSegments = Segmentation::Treshold(fGray,10);	
		IplImage* less = cvCreateImage(cvSize(fSegments->width/4,fSegments->height/4),fSegments->depth,fSegments->nChannels);
		cvResize(fSegments,less);

		int conH = ConturFr->height;
		int conW = ConturFr->width;
		int W = image->width;
		int H = image->height;
		double ratio = conW/(double)conH;

		int degree = 0;
		int x,y,h,w;
		while (degree!=W-conW)
		{
			IplImage* temp = cvCreateImage(cvSize(ConturFr->width-(int)(degree*(1/ratio)),ConturFr->height-(int)(degree*(ratio))),ConturFr->depth,ConturFr->nChannels);
			cvResize(ConturFr,temp);
			//cvShowImage("temp",temp);
			 x=0, y=0, h=0, w=0;
			if (Determinator::Compare(less,temp,x,y,h,w))	
			{
				cvReleaseImage(&temp);
				
				break;
			}
			cvReleaseImage(&temp);
			++degree;

		}
		char* Pix = image->imageData;

		x*=4;
		y*=4;
		h*=4;
		w*=4;
		cvRectangle(image,cvPoint(x,y),cvPoint(x+h,y+w),cvScalar(100,100,200),3);

		cvReleaseImage(&fGray);
		cvReleaseImage(&fSegments);
		cvReleaseImage(&less);
	}

	cvShowImage(videoWindowName,image);

}


void CamClientFrame::OnThisTimer(wxTimerEvent & event) {
	wxCriticalSectionLocker open(this->videoThreadCS);
	if (this->thisCapture)
	{
		this->photo = cvQueryFrame(this->thisCapture);
		cvShowImage("thisVideo", this->photo);
	}
}

void CamClientFrame::OnOpenThisCamera(wxCommandEvent & event) {
	wxCriticalSectionLocker open(this->videoThreadCS);
	this->thisCapture = cvCreateCameraCapture(0);
	cvNamedWindow("thisVideo", CV_WINDOW_AUTOSIZE);
	this->videoThisTimer.Start(WAIT_FOR_NEXT_FRAME);
}

void CamClientFrame::OnMakePhoto(wxCommandEvent & event) {
	wxCriticalSectionLocker open(this->videoThreadCS);
	this->videoThisTimer.Stop();
	cvSaveImage("D:/photo.jpg", this->photo);
	if (ConturFr)
	{
		cvReleaseImage(&ConturFr);	
	}
	ConturFr = cvCreateImage(cvSize(photo->width,photo->height),photo->depth,photo->nChannels);
	ConturFr = cvCloneImage(photo);
	cvSmooth( ConturFr, ConturFr, CV_GAUSSIAN, 3, 3 );
	CvSize size = cvSize(ConturFr->width, ConturFr->height);
	IplImage* fGray = cvCreateImage(size,ConturFr->depth,1);	
	cvCvtColor(ConturFr,fGray,CV_RGB2GRAY);
	IplImage* fSegments = Segmentation::Treshold(fGray,10);	
	cvReleaseImage(&ConturFr);
	ConturFr = cvCreateImage(cvSize(fSegments->width,fSegments->height),fSegments->depth,1);
	ConturFr = cvCloneImage(fSegments);
	cvReleaseImage(&fGray);
	cvReleaseImage(&fSegments);
	
	cvDestroyWindow("thisVideo");
	cvReleaseCapture(&this->thisCapture);
}

void CamClientFrame::OnNewTraining(wxCommandEvent & event) {
	event.Skip(true);
	if (TrainingFrame::instance == 0) {
		TrainingFrame::instance = new TrainingFrame(this);
		TrainingFrame::instance->Show();
	}
	TrainingFrame::instance->SetFocus();
}

void CamClientFrame::OnOpenExistingSession(wxCommandEvent & event) {
	event.Skip(false);
	SessionsListFrame dlg(this);
	if (dlg.ShowModal() == wxID_OK) {
		wxMessageBox(_("Session loaded successfully"), _("Info."), wxICON_INFORMATION);
	}
}